﻿using System;
using System.Collections.Generic;
using dnAnalytics.Statistics;
using NUnit.Framework;

namespace dnAnalytics.Tests.Statistics
{
    [TestFixture]
    public class DescriptiveStatisticsTests
    {
        private readonly IDictionary<string, StatTestData> mData = new Dictionary<string, StatTestData>();

        public DescriptiveStatisticsTests()
        {
            StatTestData lottery = new StatTestData("./TestData/NIST/Lottery.dat");
            mData.Add("lottery", lottery);
            StatTestData lew = new StatTestData("./TestData/NIST/Lew.dat");
            mData.Add("lew", lew);
            StatTestData mavro = new StatTestData("./TestData/NIST/Mavro.dat");
            mData.Add("mavro", mavro);
            StatTestData michelso = new StatTestData("./TestData/NIST/Michelso.dat");
            mData.Add("michelso", michelso);
            StatTestData numacc1 = new StatTestData("./TestData/NIST/NumAcc1.dat");
            mData.Add("numacc1", numacc1);
            StatTestData numacc2 = new StatTestData("./TestData/NIST/NumAcc2.dat");
            mData.Add("numacc2", numacc2);
            StatTestData numacc3 = new StatTestData("./TestData/NIST/NumAcc3.dat");
            mData.Add("numacc3", numacc3);
            StatTestData numacc4 = new StatTestData("./TestData/NIST/NumAcc4.dat");
            mData.Add("numacc4", numacc4);
        }

        [Test]
        public void Constructor_ThrowArgumentNullException()
        {
            const IEnumerable<double> data = null;
            const IEnumerable<double?> nullableData = null;

            Assert.Throws<ArgumentNullException>(() => new DescriptiveStatistics(data));
            Assert.Throws<ArgumentNullException>(() => new DescriptiveStatistics(data, true));
            Assert.Throws<ArgumentNullException>(() => new DescriptiveStatistics(nullableData));
            Assert.Throws<ArgumentNullException>(() => new DescriptiveStatistics(nullableData, true));
        }

        [Test]
        [TestCase("lottery", 15, -0.09333165310779, -1.19256091074856, 522.5, 4, 999, 218)]
        [TestCase("lew", 15, -0.050606638756334, -1.49604979214447, -162, -579, 300, 200)]
        [TestCase("mavro", 12, 0.64492948110824, -0.82052379677456, 2.0018, 2.0013, 2.0027, 50)]
        [TestCase("michelso", 12, -0.0185388637725746, 0.33968459842539, 299.85, 299.62, 300.07, 100)]
        [TestCase("numacc1", 15, 0, 0, 10000002, 10000001, 10000003, 3)]
        [TestCase("numacc2", 14, 0, -2.003003003003, 1.2, 1.1, 1.3, 1001)]
        [TestCase("numacc3", 9, 0, -2.003003003003, 1000000.2, 1000000.1, 1000000.3, 1001)]
        [TestCase("numacc4", 8, 0, -2.00300300299913, 10000000.2, 10000000.1, 10000000.3, 1001)]
        public void IEnumerableDouble(string dataSet, int digits, double skewness, double kurtosis, double median, double min, double max, int count)
        {
            StatTestData data = mData[dataSet];
            DescriptiveStatistics stats = new DescriptiveStatistics(data.Data);

            TestHelper.TestSignificantDigits(data.Mean, stats.Mean, 15);
            TestHelper.TestSignificantDigits(data.StandardDeviation, stats.StandardDeviation, digits);
            TestHelper.TestSignificantDigits(skewness, stats.Skewness, 7);
            TestHelper.TestSignificantDigits(kurtosis, stats.Kurtosis, 7);
            TestHelper.TestSignificantDigits(median, stats.Median, 15);
            Assert.That(stats.Minimum, Is.EqualTo(min));
            Assert.That(stats.Maximum, Is.EqualTo(max));
            Assert.That(stats.Count, Is.EqualTo(count));
        }

        [Test]
        [TestCase("lottery", -0.09333165310779, -1.19256091074856, 522.5, 4, 999, 218)]
        [TestCase("lew", -0.050606638756334, -1.49604979214447, -162, -579, 300, 200)]
        [TestCase("mavro", 0.64492948110824, -0.82052379677456, 2.0018, 2.0013, 2.0027, 50)]
        [TestCase("michelso", -0.0185388637725746, 0.33968459842539, 299.85, 299.62, 300.07, 100)]
        [TestCase("numacc1", 0, 0, 10000002, 10000001, 10000003, 3)]
        [TestCase("numacc2", 0, -2.003003003003, 1.2, 1.1, 1.3, 1001)]
        [TestCase("numacc3", 0, -2.003003003003, 1000000.2, 1000000.1, 1000000.3, 1001)]
        [TestCase("numacc4", 0, -2.00300300299913, 10000000.2, 10000000.1, 10000000.3, 1001)]
        public void IEnumerableDoubleHighAccuracy(string dataSet, double skewness, double kurtosis, double median, double min, double max, int count)
        {
            StatTestData data = mData[dataSet];
            DescriptiveStatistics stats = new DescriptiveStatistics(data.Data, true);
            TestHelper.TestSignificantDigits(data.Mean, stats.Mean, 15);
            TestHelper.TestSignificantDigits(data.StandardDeviation, stats.StandardDeviation, 15);
            TestHelper.TestSignificantDigits(skewness, stats.Skewness, 9);
            TestHelper.TestSignificantDigits(kurtosis, stats.Kurtosis, 9);
            TestHelper.TestSignificantDigits(median, stats.Median, 15);
            Assert.That(stats.Minimum, Is.EqualTo(min));
            Assert.That(stats.Maximum, Is.EqualTo(max));
            Assert.That(stats.Count, Is.EqualTo(count));
        }

        [Test]
        [TestCase("lottery", 15, -0.09333165310779, -1.19256091074856, 522.5, 4, 999, 218)]
        [TestCase("lew", 15, -0.050606638756334, -1.49604979214447, -162, -579, 300, 200)]
        [TestCase("mavro", 12, 0.64492948110824, -0.82052379677456, 2.0018, 2.0013, 2.0027, 50)]
        [TestCase("michelso", 12, -0.0185388637725746, 0.33968459842539, 299.85, 299.62, 300.07, 100)]
        [TestCase("numacc1", 15, 0, 0, 10000002, 10000001, 10000003, 3)]
        [TestCase("numacc2", 14, 0, -2.003003003003, 1.2, 1.1, 1.3, 1001)]
        [TestCase("numacc3", 9, 0, -2.003003003003, 1000000.2, 1000000.1, 1000000.3, 1001)]
        [TestCase("numacc4", 8, 0, -2.00300300299913, 10000000.2, 10000000.1, 10000000.3, 1001)]
        public void IEnumerableDoubleLowAccuracy(string dataSet, int digits, double skewness, double kurtosis, double median, double min, double max, int count)
        {
            StatTestData data = mData[dataSet];
            DescriptiveStatistics stats = new DescriptiveStatistics(data.Data, false);
            TestHelper.TestSignificantDigits(data.Mean, stats.Mean, 15);
            TestHelper.TestSignificantDigits(data.StandardDeviation, stats.StandardDeviation, digits);
            TestHelper.TestSignificantDigits(skewness, stats.Skewness, 7);
            TestHelper.TestSignificantDigits(kurtosis, stats.Kurtosis, 7);
            TestHelper.TestSignificantDigits(median, stats.Median, 15);
            Assert.That(stats.Minimum, Is.EqualTo(min));
            Assert.That(stats.Maximum, Is.EqualTo(max));
            Assert.That(stats.Count, Is.EqualTo(count));
        }

        [Test]
        [TestCase("lottery", 15, -0.09333165310779, -1.19256091074856, 522.5, 4, 999, 218)]
        [TestCase("lew", 15, -0.050606638756334, -1.49604979214447, -162, -579, 300, 200)]
        [TestCase("mavro", 12, 0.64492948110824, -0.82052379677456, 2.0018, 2.0013, 2.0027, 50)]
        [TestCase("michelso", 12, -0.0185388637725746, 0.33968459842539, 299.85, 299.62, 300.07, 100)]
        [TestCase("numacc1", 15, 0, 0, 10000002, 10000001, 10000003, 3)]
        [TestCase("numacc2", 14, 0, -2.003003003003, 1.2, 1.1, 1.3, 1001)]
        [TestCase("numacc3", 9, 0, -2.003003003003, 1000000.2, 1000000.1, 1000000.3, 1001)]
        [TestCase("numacc4", 8, 0, -2.00300300299913, 10000000.2, 10000000.1, 10000000.3, 1001)]
        public void IEnumerableNullableDouble(string dataSet, int digits, double skewness, double kurtosis, double median, double min, double max, int count)
        {
            StatTestData data = mData[dataSet];
            DescriptiveStatistics stats = new DescriptiveStatistics(data.DataWithNulls);
            TestHelper.TestSignificantDigits(data.Mean, stats.Mean, 15);
            TestHelper.TestSignificantDigits(data.StandardDeviation, stats.StandardDeviation, digits);
            TestHelper.TestSignificantDigits(skewness, stats.Skewness, 7);
            TestHelper.TestSignificantDigits(kurtosis, stats.Kurtosis, 7);
            TestHelper.TestSignificantDigits(median, stats.Median, 15);
            Assert.That(stats.Minimum, Is.EqualTo(min));
            Assert.That(stats.Maximum, Is.EqualTo(max));
            Assert.That(stats.Count, Is.EqualTo(count));
        }

        [Test]
        [TestCase("lottery", -0.09333165310779, -1.19256091074856, 522.5, 4, 999, 218)]
        [TestCase("lew", -0.050606638756334, -1.49604979214447, -162, -579, 300, 200)]
        [TestCase("mavro", 0.64492948110824, -0.82052379677456, 2.0018, 2.0013, 2.0027, 50)]
        [TestCase("michelso", -0.0185388637725746, 0.33968459842539, 299.85, 299.62, 300.07, 100)]
        [TestCase("numacc1", 0, 0, 10000002, 10000001, 10000003, 3)]
        [TestCase("numacc2", 0, -2.003003003003, 1.2, 1.1, 1.3, 1001)]
        [TestCase("numacc3", 0, -2.003003003003, 1000000.2, 1000000.1, 1000000.3, 1001)]
        [TestCase("numacc4", 0, -2.00300300299913, 10000000.2, 10000000.1, 10000000.3, 1001)]
        public void IEnumerableNullableDoubleHighAccuracy(string dataSet, double skewness, double kurtosis, double median, double min, double max, int count)
        {
            StatTestData data = mData[dataSet];
            DescriptiveStatistics stats = new DescriptiveStatistics(data.DataWithNulls, true);
            TestHelper.TestSignificantDigits(data.Mean, stats.Mean, 15);
            TestHelper.TestSignificantDigits(data.StandardDeviation, stats.StandardDeviation, 15);
            TestHelper.TestSignificantDigits(skewness, stats.Skewness, 9);
            TestHelper.TestSignificantDigits(kurtosis, stats.Kurtosis, 9);
            TestHelper.TestSignificantDigits(median, stats.Median, 15);
            Assert.That(stats.Minimum, Is.EqualTo(min));
            Assert.That(stats.Maximum, Is.EqualTo(max));
            Assert.That(stats.Count, Is.EqualTo(count));
        }

        [Test]
        [TestCase("lottery", 15, -0.09333165310779, -1.19256091074856, 522.5, 4, 999, 218)]
        [TestCase("lew", 15, -0.050606638756334, -1.49604979214447, -162, -579, 300, 200)]
        [TestCase("mavro", 12, 0.64492948110824, -0.82052379677456, 2.0018, 2.0013, 2.0027, 50)]
        [TestCase("michelso", 12, -0.0185388637725746, 0.33968459842539, 299.85, 299.62, 300.07, 100)]
        [TestCase("numacc1", 15, 0, 0, 10000002, 10000001, 10000003, 3)]
        [TestCase("numacc2", 14, 0, -2.003003003003, 1.2, 1.1, 1.3, 1001)]
        [TestCase("numacc3", 9, 0, -2.003003003003, 1000000.2, 1000000.1, 1000000.3, 1001)]
        [TestCase("numacc4", 8, 0, -2.00300300299913, 10000000.2, 10000000.1, 10000000.3, 1001)]
        public void IEnumerableNullableDoubleLowAccuracy(string dataSet, int digits, double skewness, double kurtosis, double median, double min, double max, int count)
        {
            StatTestData data = mData[dataSet];
            DescriptiveStatistics stats = new DescriptiveStatistics(data.DataWithNulls, false);
            TestHelper.TestSignificantDigits(data.Mean, stats.Mean, 15);
            TestHelper.TestSignificantDigits(data.StandardDeviation, stats.StandardDeviation, digits);
            TestHelper.TestSignificantDigits(skewness, stats.Skewness, 7);
            TestHelper.TestSignificantDigits(kurtosis, stats.Kurtosis, 7);
            TestHelper.TestSignificantDigits(median, stats.Median, 15);
            Assert.That(stats.Minimum, Is.EqualTo(min));
            Assert.That(stats.Maximum, Is.EqualTo(max));
            Assert.That(stats.Count, Is.EqualTo(count));
        }
    }
}